package com.sky.service.impl;
import cn.hutool.json.JSONUtil;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.dto.InstructorLoginDTO;
import com.sky.entity.Instructor;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.CommonException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.InstructorMapeer;
import com.sky.service.DepartmentService;
import com.sky.service.UserLoginService;
import com.sky.utils.HttpClientUtil;
import com.sky.utils.Permission;
import com.sky.vo.InstructorInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserLoginServiceImpl implements UserLoginService {
    private final static String PHONE_URL = "https://push.spug.cc/send/XlxGd8JwwMm2bn0R";
    @Autowired
    private InstructorMapeer instructorMapeer;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 用户登录
     * @param instructorLoginDTO 参数为工号，密码，权限级别
     * @return
     */
    @Override
    public Instructor login(InstructorLoginDTO instructorLoginDTO) {
        //===============手机号登录=============
        if (instructorLoginDTO.getPhone() != null && !instructorLoginDTO.getPhone().isEmpty()) {
            // 根据手机号拿到redis里面对应的验证码
            String phone = instructorLoginDTO.getPhone();
            String inputCode = instructorLoginDTO.getCode();
            String codeKey = "phone:code:" + phone;
            // 从 Redis 取出验证码
            Object redisCodeObj = redisTemplate.opsForValue().get(codeKey);
            if (redisCodeObj == null) {
                throw new CommonException("验证码已过期，请重新获取！");
            }
            String redisCode = redisCodeObj.toString();
            // 校验验证码是否一致
            if (!redisCode.equals(inputCode)) {
                throw new CommonException("验证码错误，请重新输入！");
            }
            Instructor result = instructorMapeer.getInfoByPhone(phone);
            if (result == null) {
                throw new AccountNotFoundException(MessageConstant.ACCOUNT_LOCKED);
            }
            return result;
        } else {
            // ============== 账号登录 =============
            String acount = instructorLoginDTO.getEmployeeNumber();
            String password = instructorLoginDTO.getPassword();
            Instructor result = instructorMapeer.getInfoByEID(acount);
            if (result == null) {
                throw new AccountNotFoundException(MessageConstant.PASSWORD_ERROR);
            }
            password = DigestUtils.md5DigestAsHex(password.getBytes());
            if (!password.equals(result.getPassword())) {
                throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
            }
            if (result.getStatus() == StatusConstant.DISABLE) {
                throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
            }
            return result;
        }
    }
    /**
     * 获取用户姓名和id 远程搜索
     * @param name 模糊字 姓名
     * @return
     */
    @Override
    public List<Map<String, Object>> getUserName(String name) {
        List<Instructor> instructors = instructorMapeer.findByName(name);
        List<Map<String, Object>> result = new ArrayList<>();
        for (Instructor instructor : instructors) {
            Map<String, Object> userMap = new HashMap<>();
            userMap.put("instructorId", instructor.getId());
            userMap.put("departmentLeader", instructor.getName());
            result.add(userMap);
        }
        return result;
    }

    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    @Override
    public InstructorInfoVO getUserInfo(String userId) {
        Instructor instructor = instructorMapeer.getInfoById(userId);
        log.info("获取用户信息，用户id：{}", instructor);
        String departmentName = departmentService.buildDepartmentPath(instructor.getDepartmentId());
        InstructorInfoVO result = new InstructorInfoVO();
        BeanUtils.copyProperties(instructor, result);
        result.setDepartment(departmentName)
                .setPermissions(JSONUtil.toList(JSONUtil.parseArray(instructor.getPermissions()), String.class))
                .setRoles(JSONUtil.toList(JSONUtil.parseArray(instructor.getRoles()), String.class))
        ;
        return result;
    }

    /**
     * 获取手机号验证码
     * @param phone
     */
    @Override
    public String getPhoneCode(String phone) throws IOException {
        Instructor result = instructorMapeer.getInfoByPhone(phone);
        if (result == null) {
            throw new CommonException(MessageConstant.Phone_err);
        }
        // Redis key
        String codeKey = "phone:code:" + phone;
        String timeKey = "phone:code:time:" + phone;
        // 查询发送时间
        Object timeValue = redisTemplate.opsForValue().get(timeKey);
        if (timeValue != null) {
            long lastTime = Long.parseLong(timeValue.toString());
            long now = System.currentTimeMillis();
            if (now - lastTime < 60 * 1000) {
               throw new CommonException("验证码请求过于频繁，请1分钟后再试");
            }else {
                // 超过1分钟，清除旧数据
                redisTemplate.delete(codeKey);
                redisTemplate.delete(timeKey);
            }
        }
        // 生成 6 位验证码
        String code = String.format("%06d", new Random().nextInt(999999));
        Map<String, String> map = Map.of(
                "code", code,
                "targets", phone
        );
        String resultCode =  HttpClientUtil.doPost(PHONE_URL,map);
        redisTemplate.opsForValue().set(codeKey, code, 30, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(timeKey, String.valueOf(System.currentTimeMillis()), 30, TimeUnit.MINUTES);
        return resultCode;
    }
}
